package com.opNaga.bpmp.flowObject.activity;

import com.opNaga.bpmp.BPMPElement;
import com.opNaga.bpmp.connectingObject.sequenceFlow.SequenceFlowSource;
import com.opNaga.bpmp.connectingObject.sequenceFlow.SequenceFlowTarget;
import com.opNaga.bpmp.flowObject.event.EndEvent;
import com.opNaga.bpmp.flowObject.event.HasEnd;
import com.opNaga.bpmp.flowObject.event.HasStart;
import com.opNaga.bpmp.flowObject.event.StartEvent;
import com.opNaga.bpmp.swimlane.Containable;
import com.opNaga.bpmp.swimlane.Container;

import java.util.*;

/**
 * User: Junyi BEI
 * Date: 19/01/2012
 * Time: 13:59
 */
public class SubProcess extends Activity implements Container, HasStart, HasEnd {
  private Set<BPMPElement> elements = new HashSet<BPMPElement>();
  private Set<BPMPElement> starts = new HashSet<BPMPElement>();
  private Set<BPMPElement> ends = new HashSet<BPMPElement>();

  public SubProcess() {
    super();
  }

  public SubProcess(String t) {
    super(t);
  }

  @Override
  public String getType() {
    return "Sub Process";
  }

  @Override
  public boolean destroy() {
    Iterator<BPMPElement> beIt = iterateElements();
    while(beIt.hasNext()) {
      BPMPElement be = beIt.next();
      if(!be.destroy()) {
        return standardMessage(820, be);
      }
    }
    starts.clear();
    ends.clear();
    return super.destroy();
  }

  public BPMPElement getParent() {
    return null;
  }

  public boolean isRoot() {
    return false;
  }

  public boolean addElement(BPMPElement be) {
    if(!Containable.class.isAssignableFrom(be.getClass())) {
      return standardMessage(550, be);
    }
    if(!((Containable) be).setContainer(this)) {
      return standardMessage(551, be);
    }
    if(be instanceof StartEvent) {
      if(!registerStart(be)) {
        return standardMessage(553, be);
      }
    }
    if(be instanceof EndEvent) {
      if(!registerEnd(be)) {
        return standardMessage(554, be);
      }
    }
    if(!elements.add(be)) {
      return standardMessage(552, be);
    }
    return standardMessage(590, be);
  }

  public boolean contains(BPMPElement be) {
    return elements.contains(be);
  }

  public boolean removeElement(BPMPElement be) {
    if(!Containable.class.isAssignableFrom(be.getClass())) {
      return standardMessage(560, be);
    }
    if(!((Containable) be).setContainer(null)) {
      return standardMessage(561, be);
    }
    if(be instanceof StartEvent) {
      if(!deregisterStart(be)) {
        return standardMessage(563, be);
      }
    }
    if(be instanceof EndEvent) {
      if(!deregisterEnd(be)) {
        return standardMessage(564, be);
      }
    }
    if(!elements.remove(be)) {
      return standardMessage(562, be);
    }
    return standardMessage(591, be);
  }

  public boolean isEmpty() {
    return elements.isEmpty();
  }

  public int size() {
    return elements.size();
  }

  public Iterator<BPMPElement> iterateElements() {
    return new ArrayList<BPMPElement>(elements).iterator();
  }

  public boolean registerStart(BPMPElement be) {
    if(!StartEvent.class.isAssignableFrom(be.getClass())) {
      return standardMessage(400, be);
    }
    if(!starts.add(be)) {
      return standardMessage(401, be);
    }
    return true;
  }

  public boolean deregisterStart(BPMPElement be) {
    if(!StartEvent.class.isAssignableFrom(be.getClass())) {
      return standardMessage(410, be);
    }
    if(!starts.remove(be)) {
      return standardMessage(411, be);
    }
    return true;
  }

  public Iterator<BPMPElement> iterateStarts() {
    if(!isEmpty() && isStartEmpty()) {
      Iterator<BPMPElement> elementIt = iterateElements();
      Collection<BPMPElement> defaultStarts = new HashSet<BPMPElement>();
      while(elementIt.hasNext()) {
        BPMPElement element = elementIt.next();
        if(SequenceFlowTarget.class.isAssignableFrom(element.getClass()) && ((SequenceFlowTarget)element).isInwardSequenceFlowEmpty()) {
          defaultStarts.add(element);
        }
      }
      return defaultStarts.iterator();
    }
    return new ArrayList<BPMPElement>(starts).iterator();
  }

  public boolean isStartEmpty() {
    return starts.isEmpty();
  }

  public int startSize() {
    return starts.size();
  }

  public boolean registerEnd(BPMPElement be) {
    if(!(be instanceof EndEvent)) {
      return standardMessage(420, be);
    }
    if(!ends.add(be)) {
      return standardMessage(421, be);
    }
    return true;
  }

  public boolean deregisterEnd(BPMPElement be) {
    if(!(be instanceof EndEvent)) {
      return standardMessage(430, be);
    }
    if(!starts.remove(be)) {
      return standardMessage(431, be);
    }
    return true;
  }

  public Iterator<BPMPElement> iterateEnds() {
    if(!isEmpty() && isStartEmpty()) {
      Iterator<BPMPElement> elementIt = iterateElements();
      Collection<BPMPElement> defaultEnds = new HashSet<BPMPElement>();
      while(elementIt.hasNext()) {
        BPMPElement element = elementIt.next();
        if(SequenceFlowSource.class.isAssignableFrom(element.getClass()) && ((SequenceFlowSource)element).isOutwardSequenceFlowEmpty()) {
          defaultEnds.add(element);
        }
      }
      return defaultEnds.iterator();
    }
    return new ArrayList<BPMPElement>(ends).iterator();
  }

  public boolean isEndEmpty() {
    return ends.isEmpty();
  }

  public int endSize() {
    return ends.size();
  }
}
